package com.xiaopo.flying.sticker;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.text.SimpleTextLayout;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;

/**
 * Customize your sticker with text and image background.
 * You can place some text into a given region, however,
 * you can also add a plain text sticker. To support text
 * auto resizing , I take most of the code from AutoResizeTextView.
 * See https://adilatwork.blogspot.com/2014/08/ohos-textview-which-resizes-its-text.html
 * Notice: It's not efficient to add long text due to too much of
 * StaticLayout object allocation.
 * Created by liutao on 30/11/2016.
 */

public class TextSticker extends Sticker {

    /**
     * Our ellipsis string.
     */
    private static final String mEllipsis = "\u2026";

    private final Context context;
    private final Rect realBounds;
    private final Rect textRect;
    private final Paint textPaint;
    private Element drawable;
    private SimpleTextLayout staticLayout;
    private String text;

    /**
     * Upper bounds for text size.
     * This acts as a starting point for resizing.
     */
    private int maxTextSizePixels;

    /**
     * Lower bounds for text size.
     */
    private int minTextSizePixels;

    public TextSticker(Context context) {
        this(context, null);
    }

    public TextSticker(Context context, Element drawable) {
        this.context = context;
        this.drawable = drawable;
        if (drawable == null) {
            this.drawable = new ShapeElement(context, ResourceTable.Graphic_sticker_transparent_background);
        }
        textPaint = new Paint();
        textPaint.setTextAlign(TextAlignment.HORIZONTAL_CENTER);
        textPaint.setMultipleLine(true);
        realBounds = new Rect(0, 0, getWidth(), getHeight());
        textRect = new Rect(0, 0, getWidth(), getHeight());
        minTextSizePixels = convertFpToPx(6);
        maxTextSizePixels = convertFpToPx(32);
        textPaint.setTextSize(maxTextSizePixels);
    }

    @Override
    public void drawPixelMap(Canvas canvas) {
        Matrix matrix = getMatrix();
        canvas.save();
        canvas.concat(matrix);
        if (drawable != null) {
            drawable.setBounds(realBounds);
            RectFloat rectFloat = new RectFloat();
            rectFloat.left = realBounds.left;
            rectFloat.top = realBounds.top;
            rectFloat.right = realBounds.right;
            rectFloat.bottom = realBounds.bottom;
            if (drawable instanceof PixelMapElement) {
                canvas.drawPixelMapHolderRect(new PixelMapHolder(((PixelMapElement) drawable).getPixelMap()), rectFloat, new Paint());
            }
        }
        canvas.restore();
        canvas.save();
        canvas.concat(matrix);
        if (textRect.getWidth() == getWidth()) {
            int dy = getHeight() / 2 - staticLayout.getHeight() / 2;
            // center vertical
            canvas.translate(0, dy);
        } else {
            int dx = textRect.left;
            int dy = textRect.top + textRect.getHeight() / 2 - staticLayout.getHeight() / 2;
            canvas.translate(dx, dy);
        }
        staticLayout.drawText(canvas);
        canvas.restore();
    }

    @Override
    public void draw(Canvas canvas) {
        Matrix matrix = getMatrix();
        canvas.save();
        canvas.concat(matrix);
        if (drawable != null) {
            drawable.setBounds(realBounds);
            drawable.drawToCanvas(canvas);
        }
        canvas.restore();
        canvas.save();
        canvas.concat(matrix);
        if (textRect.getWidth() == getWidth()) {
            int dy = getHeight() / 2 - staticLayout.getHeight() / 2;
            // center vertical
            canvas.translate(0, dy);
        } else {
            int dx = textRect.left;
            int dy = textRect.top + textRect.getHeight() / 2 - staticLayout.getHeight() / 2;
            canvas.translate(dx, dy);
        }
        staticLayout.drawText(canvas);
        canvas.restore();
    }

    @Override
    public int getWidth() {
        return drawable.getWidth();
    }

    @Override
    public int getHeight() {
        return drawable.getHeight();
    }

    @Override
    public void release() {
        super.release();
        if (drawable != null) {
            drawable = null;
        }
    }

    @Override
    public TextSticker setAlpha(int alpha) {
        textPaint.setAlpha(alpha);
        return this;
    }

    @Override
    public Element getDrawable() {
        return drawable;
    }

    @Override
    public TextSticker setDrawable(Element drawable) {
        this.drawable = drawable;
        realBounds.set(0, 0, getWidth(), getHeight());
        textRect.set(0, 0, getWidth(), getHeight());
        return this;
    }

    public TextSticker setDrawable(Element drawable, Rect region) {
        this.drawable = drawable;
        realBounds.set(0, 0, getWidth(), getHeight());
        if (region == null) {
            textRect.set(0, 0, getWidth(), getHeight());
        } else {
            textRect.set(region.left, region.top, region.right, region.bottom);
        }
        return this;
    }

    public TextSticker setTextColor(int color) {
        textPaint.setColor(new Color(color));
        staticLayout = new SimpleTextLayout(text, textPaint, textRect, drawable.getWidth());
        return this;
    }

    public TextSticker setTextAlign(int alignment) {
        return this;
    }

    public TextSticker setMaxTextSize(float size) {
        textPaint.setTextSize(AttrHelper.fp2px(size, context));
        maxTextSizePixels = textPaint.getTextSize();
        return this;
    }

    /**
     * Sets the lower text size limit
     *
     * @param minTextSizeScaledPixels the minimum size to use for text in this view,in scaled pixels.
     * @return TextSticker
     */
    public TextSticker setMinTextSize(int minTextSizeScaledPixels) {
        minTextSizePixels = minTextSizeScaledPixels;
        return this;
    }

    public TextSticker setLineSpacing(float add, float multiplier) {
        return this;
    }

    public TextSticker setText(String text) {
        this.text = text;
        return this;
    }

    public String getText() {
        return text;
    }

    /**
     * Resize this view's text size with respect to its width and height
     * (minus padding). You should always call this method after the initialization.
     *
     * @return TextSticker
     */
    public TextSticker resizeText() {
        final int availableHeightPixels = textRect.getHeight();

        final int availableWidthPixels = textRect.getWidth();

        final String text = getText();

        // Safety check
        // (Do not resize if the view does not have dimensions or if there is no text)
        if (text == null
            || text.length() <= 0
            || availableHeightPixels <= 0
            || availableWidthPixels <= 0
            || maxTextSizePixels <= 0) {
            return this;
        }

        int targetTextSizePixels = maxTextSizePixels;
        int targetTextHeightPixels =
            getTextHeightPixels(text, availableWidthPixels, targetTextSizePixels);

        // Until we either fit within our TextView
        // or we have reached our minimum text size,
        // incrementally try smaller sizes
        while (targetTextHeightPixels > availableHeightPixels
            && targetTextSizePixels > minTextSizePixels) {
            targetTextSizePixels = Math.max(targetTextSizePixels - 2, minTextSizePixels);

            targetTextHeightPixels =
                getTextHeightPixels(text, availableWidthPixels, targetTextSizePixels);
        }

        // If we have reached our minimum text size and the text still doesn't fit,
        // append an ellipsis
        // (NOTE: Auto-ellipsize doesn't work hence why we have to do it here)
        if (targetTextSizePixels == minTextSizePixels
            && targetTextHeightPixels > availableHeightPixels) {
            // Make a copy of the original TextPaint object for measuring
            Paint textPaintCopy = new Paint(textPaint);
            textPaintCopy.setTextSize(targetTextSizePixels);

            // Measure using a StaticLayout instance
            SimpleTextLayout staticLayout = new SimpleTextLayout(text, textPaintCopy, textRect, drawable.getWidth());
            // Check that we have a least one line of rendered text
            if (staticLayout.getLineCount() > 0) {
            }
        }
        textPaint.setTextSize(targetTextSizePixels);
        staticLayout = new SimpleTextLayout(this.text, textPaint, textRect, drawable.getWidth());
        return this;
    }

    /**
     * 获取最小文本大小像素
     *
     * @return lower text size limit, in pixels.
     */
    public float getMinTextSizePixels() {
        return minTextSizePixels;
    }

    /**
     * Sets the text size of a clone of the view's {@link Paint} object
     * and uses a {@link SimpleTextLayout} instance to measure the height of the text.
     *
     * @param source
     * @param availableWidthPixels
     * @param textSizePixels
     * @return the height of the text when placed in a view
     * with the specified width
     * and when the text has the specified size.
     */
    protected int getTextHeightPixels(CharSequence source, int availableWidthPixels,
                                      int textSizePixels) {
        textPaint.setTextSize(textSizePixels);
        // It's not efficient to create a StaticLayout instance
        // every time when measuring, we can use StaticLayout.Builder
        // since api 23.
        SimpleTextLayout staticLayout = new SimpleTextLayout(text, textPaint, textRect, drawable.getWidth());
        return staticLayout.getHeight();
    }

    /**
     * 将 Fp 转换为 Px
     *
     * @param scaledPixels
     * @return the number of pixels which scaledPixels corresponds to on the device.
     */
    private int convertFpToPx(float scaledPixels) {
        return AttrHelper.fp2px(scaledPixels, context);
    }
}
